﻿namespace wToggl
{
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Diagnostics;
    using System.Linq;
    using System.Net.Http;
    using System.Runtime.CompilerServices;
    using System.Threading;
    using System.Threading.Tasks;

    using Windows.Data.Xml.Dom;
    using Windows.Graphics.Display;
    using Windows.Networking.Connectivity;
    using Windows.UI.Core;
    using Windows.UI.Notifications;
    using Windows.UI.Popups;
    using Windows.UI.Xaml;
    using Windows.UI.Xaml.Controls;
    using Windows.UI.Xaml.Controls.Primitives;
    using Windows.UI.Xaml.Input;
    using Windows.UI.Xaml.Navigation;

    using wToggl.Common;

    using wToggle.Platform;
    using wToggle.Platform.Data;
    using wToggle.Platform.Interfaces;

    using XPlatUtils;

    /// <summary>
    /// A page that displays a grouped collection of items.
    /// </summary>
    public sealed partial class HubPage : INotifyPropertyChanged
    {
        private readonly NavigationHelper navigationHelper;

        private Workspace workspace;

        private string timeEntryDescription;

        private string trackingDuration = "00:00:00";

        private bool isWorking;

        private IEnumerable<Project> projects;

        private DateTime? startTime;

        private Timer timer;

        private readonly Timer workspaceReloadTimer;

        private string overlayMessage;

        private Visibility startVisibility;

        private Visibility stopVisibility = Visibility.Collapsed;

        private IEnumerable selectedTags;

        private IEnumerable selectedProjects;

        private IEnumerable<TimeEntry> recentEntries = new List<TimeEntry>();

        private bool isLoadingEntries;

        private Visibility recentEntryListVisibility = Visibility.Collapsed;

        private Visibility recentEntryProgressCircleVisibility;

        private string workTotal;

        private Project chessProject;

        private DateTime? chessStartTime;

        private bool isChessRunning;

        private string chessTrackingDuration = "00:00:00";

        private IEnumerable<Workspace> workspaces;

        public HubPage()
        {
            this.InitializeComponent();

            // Hub is only supported in Portrait orientation
            DisplayInformation.AutoRotationPreferences = DisplayOrientations.Portrait;

            this.NavigationCacheMode = NavigationCacheMode.Disabled;

            this.navigationHelper = new NavigationHelper(this);
            this.navigationHelper.LoadState += this.NavigationHelper_LoadState;
            this.navigationHelper.SaveState += this.NavigationHelper_SaveState;

            this.workspaceReloadTimer = new Timer(
                this.ReloadWorkspaceTimerCallback,
                null,
                Timeout.Infinite,
                Timeout.Infinite);
        }

        private async void ReloadWorkspaceTimerCallback(object state)
        {
            await this.LoadWorkspace(true);
        }

        private async Task LoadWorkspace(bool silent = false)
        {
            this.OverlayMessage = "Loading data...";
            this.IsWorking = true;
            this.timer = new Timer(this.TimerCallback, null, Timeout.Infinite, Timeout.Infinite);

            if (!await CacheLayer.FlushWrites())
            {
                await this.Dispatcher.ShowMessage("Some of your cached modifications could not be uploaded.", "Toggly");
            }

            var provider = ServiceContainer.Resolve<IWorkspaceProvider>();
            var settings = ServiceContainer.Resolve<IApplicationSettings>();

            try
            {
                var wspaces = await provider.GetWorkspaces();

                Workspace selectedWorkspace;
                if (settings.LastWorkspace.HasValue)
                {
                    selectedWorkspace = wspaces.FirstOrDefault(w => w.Id == settings.LastWorkspace)
                                        ?? wspaces.FirstOrDefault();
                }
                else
                {
                    selectedWorkspace = wspaces.FirstOrDefault();
                }

                if (selectedWorkspace != null)
                {
                    settings.LastWorkspace = selectedWorkspace.Id;
                    this.workspaceReloadTimer.Change(Timeout.Infinite, Timeout.Infinite);
                    await selectedWorkspace.LoadWorkspaceData();
                    this.Workspace = selectedWorkspace;
                    this.Projects = selectedWorkspace.Projects.OrderBy(p => p.Name).ToArray();
                    await this.OnPropertyChangedAsync("Projects");
                    await this.LoadRecentEntries();
                }
                else
                {
                    this.Workspace = null;
                    this.Projects = null;
                    this.SelectedTags = null;
                    this.SelectedProjects = null;
                    this.startTime = null;
                    this.workspaceReloadTimer.Change(60000, 60000);

                    if (!silent)
                    {
                        await this.Dispatcher.ShowMessage(
                            "No workspaces were found. Check your internet connectivity and try again.",
                            "Toggly");
                    }
                }
            }
            catch (InvalidOperationException e)
            {
                if (!silent && this.Frame.CurrentSourcePageType == typeof(HubPage))
                {
                    this.Dispatcher.ShowMessage("Something went wrong when loading your data.", "Oops");
                }
            }

            this.IsWorking = false;
        }

        private async Task LoadRecentEntries()
        {
            if (this.Workspace == null)
            {
                return;
            }

            var entryProvider = ServiceContainer.Resolve<ITimeEntryProvider>();
            this.IsLoadingEntries = true;
            var entries =
                (await entryProvider.GetEntries(DateTime.UtcNow.AddDays(-3), DateTime.UtcNow, true)).Where(
                    e => e.WorkspaceId == this.Workspace.Id);
            this.RecentEntries = entries.ToList();
            this.WorkTotal = TimeSpan.FromSeconds(this.recentEntries.Sum(t => t.Duration)).ToDurationString();
            this.IsLoadingEntries = false;
        }

        private void TimerCallback(object state)
        {
            if (this.startTime == null && this.chessStartTime == null)
            {
                this.timer.Change(Timeout.Infinite, Timeout.Infinite);
                return;
            }

            TimeSpan duration;
            if (this.startTime != null)
            { 
                duration = DateTime.UtcNow.Subtract(this.startTime.Value);
            }
            else if (this.chessStartTime != null)
            {
                duration = DateTime.UtcNow.Subtract(this.chessStartTime.Value);
            }

            var durationString = string.Format(
                   "{0:00}:{1:00}:{2:00}",
                   Math.Truncate(duration.TotalHours),
                   duration.Minutes,
                   duration.Seconds);

            if (this.startTime != null)
            {
                this.TrackingDuration = durationString;
            }
            else
            {
                this.ChessTrackingDuration = durationString;
            }

        }

        public string ChessTrackingDuration
        {
            get
            {
                return this.chessTrackingDuration;
            }

            set
            {
                this.chessTrackingDuration = value;
                this.OnPropertyChanged();
            }
        }

        public Workspace Workspace
        {
            get
            {
                return this.workspace;
            }

            set
            {         
                this.workspace = value;
                this.OnPropertyChanged();  
            }
        }

        public IEnumerable<Project> Projects
        {
            get
            {
                return this.projects;
            }

            private set
            {
                this.projects = value;
                this.OnPropertyChanged();
            }
        }

        /// <summary>
        /// Gets the <see cref="NavigationHelper"/> associated with this <see cref="Page"/>.
        /// </summary>
        public NavigationHelper NavigationHelper
        {
            get { return this.navigationHelper; }
        }

        public List<string> List
        {
            get
            {
                return new List<string> { "AAAAAA", "HHHHHH" };
            }
        }

        public string TimeEntryDescription
        {
            get
            {
                return this.timeEntryDescription;
            }
            set
            {
                this.timeEntryDescription = value;
                this.OnPropertyChanged();
            }
        }

        public string TrackingDuration
        {
            get
            {
                return this.trackingDuration;
            }
            set
            {
                this.trackingDuration = value;
                this.OnPropertyChanged();
            }
        }

        public bool IsWorking
        {
            get
            {
                return this.isWorking;
            }

            set
            {
                this.isWorking = value;
                this.OnPropertyChanged();

                var bottomAppBar = this.BottomAppBar;
                if (bottomAppBar != null)
                {
                    bottomAppBar.IsEnabled = !value;
                }
            }
        }

        public string OverlayMessage
        {
            get
            {
                return this.overlayMessage;
            }
            set
            {
                this.overlayMessage = value;
                this.OnPropertyChanged();
            }
        }

        public Visibility StartVisibility
        {
            get
            {
                return this.startVisibility;
            }
            set
            {
                this.startVisibility = value;
                this.OnPropertyChanged();
            }
        }

        public Visibility StopVisibility
        {
            get
            {
                return this.stopVisibility;
            }

            set
            {
                this.stopVisibility = value;
                this.OnPropertyChanged();
            }
        }

        public IEnumerable SelectedTags
        {
            get
            {
                return this.selectedTags;
            }

            set
            {
                this.selectedTags = value;
                this.OnPropertyChanged();
            }
        }

        public IEnumerable SelectedProjects
        {
            get
            {
                return this.selectedProjects;
            }

            set
            {
                this.selectedProjects = value;
                this.OnPropertyChanged();
            }
        }

        public IEnumerable<TimeEntry> RecentEntries
        {
            get
            {
                return this.recentEntries;
            }

            set
            {
                this.recentEntries = value;
                this.OnPropertyChanged();
            }
        }

        public bool IsLoadingEntries
        {
            get
            {
                return this.isLoadingEntries;
            }

            set
            {
                this.isLoadingEntries = value;

                if (value)
                {
                    this.RecentEntryProgressCircleVisibility = Visibility.Visible;
                    this.RecentEntryListVisibility = Visibility.Collapsed;
                }
                else
                {
                    this.RecentEntryProgressCircleVisibility = Visibility.Collapsed;
                    this.RecentEntryListVisibility = Visibility.Visible;
                }

                this.OnPropertyChanged();
            }
        }

        public Visibility RecentEntryProgressCircleVisibility
        {
            get
            {
                return this.recentEntryProgressCircleVisibility;
            }

            set
            {
                this.recentEntryProgressCircleVisibility = value;
                this.OnPropertyChanged();
            }
        }

        public Visibility RecentEntryListVisibility
        {
            get
            {
                return this.recentEntryListVisibility;
            }

            set
            {
                this.recentEntryListVisibility = value;
                this.OnPropertyChanged();
            }
        }

        public string WorkTotal
        {
            get
            {
                return this.workTotal;
            }

            set
            {
                this.workTotal = value;
                this.OnPropertyChanged();
            }
        }

        public bool IsChessRunning
        {
            get
            {
                return this.isChessRunning;
            }

            set
            {
                this.isChessRunning = value;
                this.OnPropertyChanged();
            }
        }

        public IEnumerable<Workspace> Workspaces
        {
            get
            {
                return this.workspaces;
            }

            set
            {
                this.workspaces = value;
                this.OnPropertyChanged();
            }
        }

        /// <summary>
        /// Populates the page with content passed during navigation.  Any saved state is also
        /// provided when recreating a page from a prior session.
        /// </summary>
        /// <param name="sender">
        /// The source of the event; typically <see cref="NavigationHelper"/>
        /// </param>
        /// <param name="e">Event data that provides both the navigation parameter passed to
        /// <see cref="Frame.Navigate(Type, object)"/> when this page was initially requested and
        /// a dictionary of state preserved by this page during an earlier
        /// session.  The state will be null the first time a page is visited.</param>
        private async void NavigationHelper_LoadState(object sender, LoadStateEventArgs e)
        {
            try
            {
                this.IsWorking = true;
                this.OverlayMessage = "Searching for credentials...";
                var settings = ServiceContainer.Resolve<IApplicationSettings>();
                var auth = ServiceContainer.Resolve<ITogglAuth>();

                if (!settings.HasLogin() || await auth.Authenticate(settings.Username, settings.Password) != AuthStatus.Success)
                {
                    this.Dispatcher.RunAsync(
                        CoreDispatcherPriority.Normal,
                        () =>
                            {
                                this.Frame.Navigate(typeof(LoginPage));
                            });

                    return;
                }

                await this.LoadWorkspace();

                if (e.PageState == null)
                {
                    return;
                }

                if (e.PageState.ContainsKey("hubSection"))
                {
                    var sectionIndex = (int)e.PageState["hubSection"];
                    this.Hub.ScrollToSection(this.Hub.Sections[sectionIndex]);
                }

                if (this.Workspace == null)
                {
                    return;
                }

                if (e.PageState.ContainsKey("startTime"))
                {
                    this.startTime = (DateTime?)e.PageState["startTime"];

                    if (this.startTime.HasValue)
                    {
                        this.StartVisibility = Visibility.Collapsed;
                        this.StopVisibility = Visibility.Visible;
                        this.timer.Change(0, 1000);
                    }
                }

                if (e.PageState.ContainsKey("chessTime"))
                {
                    this.chessStartTime = (DateTime?)e.PageState["chessTime"];
                }

                if (e.PageState.ContainsKey("chessProjectId"))
                {
                    var projectId = (ulong)e.PageState["chessProjectId"];

                    var toggles = this.Hub.FindChildren<ToggleSwitch>();

                    foreach (var toggleSwitch in toggles)
                    {
                        var p = toggleSwitch.DataContext as Project;
                        if (p != null && p.Id == projectId)
                        {
                            toggleSwitch.IsOn = true;
                            break;
                        }
                    }
                }

                if (e.PageState.ContainsKey("chessTime"))
                {
                    this.chessStartTime = (DateTime?)e.PageState["chessTime"];
                }

                if (e.PageState.ContainsKey("entryDescription"))
                {
                    this.TimeEntryDescription = e.PageState["entryDescription"] as string;
                }

                if (e.PageState.ContainsKey("selectedProjectId"))
                {
                    var projectId = (ulong)e.PageState["selectedProjectId"];
                    var project = this.Projects.FirstOrDefault(p => p.Id == projectId);
                    this.SelectedProjects = project == null ? null : new[] { project };
                }

                if (e.PageState.ContainsKey("selectedTagIds"))
                {
                    var tagIds = e.PageState["selectedTagIds"] as string;

                    if (string.IsNullOrEmpty(tagIds))
                    {
                        return;
                    }

                    var ids = tagIds.Split(',').Select(ulong.Parse);
                    this.SelectedTags = this.Workspace.Tags.Where(t => ids.Contains(t.Id));
                }
            }
            finally
            {
                this.IsWorking = false;
            }
        }

        /// <summary>
        /// Preserves state associated with this page in case the application is suspended or the
        /// page is discarded from the navigation cache.  Values must conform to the serialization
        /// requirements of <see cref="SuspensionManager.SessionState"/>.
        /// </summary>
        /// <param name="sender">The source of the event; typically <see cref="NavigationHelper"/></param>
        /// <param name="e">Event data that provides an empty dictionary to be populated with
        /// serializable state.</param>
        private void NavigationHelper_SaveState(object sender, SaveStateEventArgs e)
        {
            if (this.Workspace == null)
            {
                return;
            }

            if (this.startTime != null)
            {
                e.PageState["startTime"] = this.startTime;
            }

            if (this.chessStartTime != null)
            {
                e.PageState["chessTime"] = this.chessStartTime;
            }

            if (this.chessProject != null)
            {
                e.PageState["chessProjectId"] = this.chessProject.Id;
            }

            e.PageState["entryDescription"] = this.TimeEntryDescription;

            if (this.SelectedProjects != null)
            {
                var p = this.SelectedProjects.Cast<Project>().ToList();

                if (p.Any())
                {
                    e.PageState["selectedProjectId"] = p.First().Id;
                }
            }

            if (this.SelectedTags != null)
            {
                e.PageState["selectedTagIds"] = string.Join(",", this.SelectedTags.Cast<Tag>().Select(t => t.Id));
            }

            if (this.Hub.SectionsInView.Any())
            {
                var hubSectionIndex = this.Hub.Sections.IndexOf(this.Hub.SectionsInView[0]);
                e.PageState["hubSection"] = hubSectionIndex;
            }
        }

        #region NavigationHelper registration

        /// <summary>
        /// The methods provided in this section are simply used to allow
        /// NavigationHelper to respond to the page's navigation methods.
        /// <para>
        /// Page specific logic should be placed in event handlers for the
        /// <see cref="NavigationHelper.LoadState"/>
        /// and <see cref="NavigationHelper.SaveState"/>.
        /// The navigation parameter is available in the LoadState method
        /// in addition to page state preserved during an earlier session.
        /// </para>
        /// </summary>
        /// <param name="e">Event data that describes how this page was reached.</param>
        protected override void OnNavigatedTo(NavigationEventArgs e)
        {
            Analytics.ReportPageVisit(this);   
            this.navigationHelper.OnNavigatedTo(e);

            NetworkInformation.NetworkStatusChanged -= this.NetworkInformationOnNetworkStatusChanged;
            NetworkInformation.NetworkStatusChanged += this.NetworkInformationOnNetworkStatusChanged;
        }

        private void NetworkInformationOnNetworkStatusChanged(object sender)
        {
            if (!Toggly.HasInternetAccess())
            {
                return;
            }

            if (!CacheLayer.HasPendingChanges())
            {
                return;
            }

            this.Dispatcher.RunAsync(
                CoreDispatcherPriority.Normal,
                async () =>
                    {
                        var md =
                            new MessageDialog(
                                "You have connected to the internet. Do you wish to sync your local changes?",
                                "Toggly");
                        md.Commands.Add(
                            new UICommand(
                                "yes",
                                o =>
                                    {
                                        this.SyncWorkspace();
                                    }));
                        md.Commands.Add(new UICommand("no"));
                        md.CancelCommandIndex = 1;
                        await md.ShowAsync();
                    });
        }

        protected override void OnNavigatedFrom(NavigationEventArgs e)
        {
            this.navigationHelper.OnNavigatedFrom(e);
            NetworkInformation.NetworkStatusChanged -= this.NetworkInformationOnNetworkStatusChanged;
        }

        #endregion

        private async void LogOffButton_OnClick(object sender, RoutedEventArgs e)
        {
            if (CacheLayer.HasPendingChanges())
            {
                await this.Dispatcher.Ask(
                    "You have pending changes stored in cache. If you log off now, these changes will be lost. Do you wish to log off?",
                    "Toggly",
                    "no",
                    new UICommand("yes", command => this.LogOff()));
                return;
            }

            await this.LogOff();
        }

        private async Task LogOff()
        {
            var settings = ServiceContainer.Resolve<IApplicationSettings>();
            settings.ClearLogin();

            var auth = ServiceContainer.Resolve<ITogglAuth>();
            await auth.LogOff();

            this.Frame.Navigate(typeof(LoginPage));
        }

        public event PropertyChangedEventHandler PropertyChanged;

        private void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            this.Dispatcher.RunAsync(
                CoreDispatcherPriority.Normal,
                () =>
                    {
                        var handler = this.PropertyChanged;
                        if (handler != null)
                        {
                            handler(this, new PropertyChangedEventArgs(propertyName));
                        }
                    });
        }

        private async Task OnPropertyChangedAsync(string propertyName = null)
        {
            await this.Dispatcher.RunAsync(
                CoreDispatcherPriority.Normal,
                () =>
                {
                    var handler = this.PropertyChanged;
                    if (handler != null)
                    {
                        handler(this, new PropertyChangedEventArgs(propertyName));
                    }
                });
        }

        private void StartButton_OnClick(object sender, RoutedEventArgs e)
        {
            this.StartLiveTracking();
        }

        private void StartLiveTracking()
        {
            if (this.Workspace == null)
            {
                this.Dispatcher.ShowMessage("No workspace could be loaded, timing operation cannot start.", "Toggly");
                return;
            }

            if (this.chessStartTime != null)
            {
                this.Dispatcher.ShowMessage(
                    "You are currently using the chess tracking. Tracking two activities at the same time is not allowed.",
                    "Toggly");
                this.Hub.ScrollToSection(this.Hub.Sections[this.Hub.Sections.Count - 1]);
                return;
            }

            Analytics.ReportEvent(EventCategory.Timing, EventAction.TimingStart, "liveTimer");
            this.startTime = DateTime.UtcNow;
            this.timer.Change(1000, 1000);
            this.StartVisibility = Visibility.Collapsed;
            this.StopVisibility = Visibility.Visible;
        }

        private async void StopButton_OnClick(object sender, RoutedEventArgs e)
        {
            if (this.Workspace != null)
            {
                if (this.startTime.HasValue)
                {
                    Analytics.ReportEvent(EventCategory.Timing, EventAction.TimingStop, "liveTimer");
                    await
                        this.CreateTimeEntry(
                            this.startTime.Value,
                            DateTime.UtcNow,
                            this.GetSelectedProject(),
                            this.GetSelectedTags());
                }
            }
            else
            {
                this.Dispatcher.ShowMessage("No workspace could be loaded, timing operation cannot start.", "Toggly");
            }

            this.DiscardTracking();
        }

        private Project GetSelectedProject()
        {
            
            if (this.SelectedProjects == null)
            {
                return null;
            }

            Project p = null;
            var list = this.SelectedProjects.Cast<Project>().ToList();
            if (list.Any())
            {
                p = list.First();
            }

            return p;
        }

        private async Task<TimeEntry> CreateTimeEntry(
            DateTime startUtc,
            DateTime endUtc,
            Project project,
            IEnumerable<Tag> tags,
            bool background = false)
        {
            try
            {
                if (!background)
                {
                    this.IsWorking = true;
                    this.OverlayMessage = "Uploading entry...";
                }
   
                var duration = endUtc.Subtract(startUtc);

                var provider = ServiceContainer.Resolve<ITimeEntryProvider>();

                var description = this.TimeEntryDescription;
                var workspaceId = this.Workspace.Id;

                var entry = new TimeEntry
                                {
                                    Description = description,
                                    WorkspaceId = workspaceId,
                                    ProjectId = project == null ? 0 : project.Id,
                                    IsBillable = false,
                                    StartIso = startUtc.ToString("o"),
                                    Duration = (int)Math.Truncate(duration.TotalSeconds),
                                    TagIds = tags != null ? tags.Select(t => t.Name).ToList() : null
                                };

                entry = await provider.Create(entry);

                if (entry == null)
                {
                    await this.Dispatcher.ShowMessage("Entry creation failed.", "Toggly");
                    return null;
                }

                ShowEntryCreationToast();
                this.LoadRecentEntries();
                return entry;
            }
            finally
            {
                if (!background)
                {
                    this.IsWorking = false;
                }
            }
        }

        private static void ShowEntryCreationToast()
        {
            const ToastTemplateType ToastTemplate = ToastTemplateType.ToastText02;

            var toastXml = ToastNotificationManager.GetTemplateContent(ToastTemplate);

            var toastTextElements = toastXml.GetElementsByTagName("text");
            toastTextElements[0].AppendChild(toastXml.CreateTextNode("Toggly"));
            toastTextElements[1].AppendChild(toastXml.CreateTextNode("Your time entry has been saved"));

            var toastNode = toastXml.SelectSingleNode("/toast");
            var audio = toastXml.CreateElement("audio");

            audio.SetAttribute("silent", "true");
            toastNode.AppendChild(audio);

            var toast = new ToastNotification(toastXml)
                            {
                                Tag = "TogglyToast",
                                ExpirationTime = new DateTimeOffset(DateTime.Now.AddSeconds(5))
                            };

            ToastNotificationManager.CreateToastNotifier().Show(toast);
        }

        private async void ManualTimeButton_OnClick(object sender, RoutedEventArgs e)
        {
            if (this.Workspace == null)
            {
                await this.Dispatcher.ShowMessage("No workspace could be loaded, time entry creation disabled.", "Toggly");
                return;
            }

            var dialog = new ManualEntryDialog();
            var result = await dialog.ShowAsync();

            if (result != ContentDialogResult.Primary)
            {
                return;
            }

            var startUtc = dialog.Start.ToUniversalTime();
            var endUtc = dialog.End.ToUniversalTime();

            await this.CreateTimeEntry(startUtc, endUtc, this.GetSelectedProject(), this.GetSelectedTags());
            this.DiscardTracking();
        }

        private List<Tag> GetSelectedTags()
        {
            var tags = this.SelectedTags != null ? this.SelectedTags.Cast<Tag>().ToList() : new List<Tag>();
            return tags;
        }

        private void TimeEntryGrid_OnHolding(object sender, HoldingRoutedEventArgs e)
        {
            var obj = sender as FrameworkElement;

            if (obj == null)
            {
                return;
            }

            FlyoutBase.ShowAttachedFlyout(obj);  
        }

        private void DetailsEntryMenu_OnClick(object sender, RoutedEventArgs e)
        {
            var menuItem = sender as MenuFlyoutItem;

            if (menuItem == null)
            {
                return;
            }

            this.Frame.Navigate(typeof(TimeEntryDetailPage), ((TimeEntry)menuItem.DataContext).Id);
        }

        private async void DeleteEntryMenu_OnClick(object sender, RoutedEventArgs e)
        {
            var menuItem = sender as MenuFlyoutItem;

            if (menuItem == null)
            {
                return;
            }

            var entry = (TimeEntry)menuItem.DataContext;
            var md = new MessageDialog("Do you really wish to delete the selected time entry?", "Delete entry?");
            md.Commands.Add(
                new UICommand(
                    "yes",
                     async o =>
                        {
                            try
                            {
                                var provider = ServiceContainer.Resolve<ITimeEntryProvider>();
                                await provider.Delete(entry.Id);

                                var list = this.RecentEntries.ToList();
                                list.Remove(entry);
                                this.RecentEntries = list;
                            }
                            catch (HttpRequestException)
                            {
                                this.Dispatcher.ShowMessage(
                                    "Attempt to delete the specified entry failed. Check your internet connection and try again.",
                                    "Toggly");
                            }
                        }));
            md.Commands.Add(new UICommand("no"));
            md.CancelCommandIndex = 1;
            md.DefaultCommandIndex = 1;
            await md.ShowAsync();
        }

        private void TimeEntryGrid_OnTapped(object sender, TappedRoutedEventArgs e)
        {
            var entry = sender.GetDataContext<TimeEntry>();

            if (entry == null)
            {
                return;
            }

            this.Frame.Navigate(typeof(TimeEntryDetailPage), entry.Id);
        }

        private async void ChessSwitch_OnToggled(object sender, RoutedEventArgs e)
        {
            var toggle = sender as ToggleSwitch;

            if (toggle == null)
            {
                return;
            }

            var p = toggle.DataContext as Project;

            if (p == null)
            {
                return;
            }

            if (toggle.IsOn)
            {
                if (this.startTime != null)
                {
                    await this.Dispatcher.ShowMessage(
                        "You are currently using the live tracking. Tracking two activities at the same time is not allowed.",
                        "Toggly");
                    toggle.IsOn = false;
                    this.Hub.ScrollToSection(this.Hub.Sections[0]);
                    return;
                }

                this.IsChessRunning = true;

                if (this.chessProject != null && this.chessStartTime != null)
                {
                    Analytics.ReportEvent(EventCategory.Timing, EventAction.TimingStop, "chessTimer");
                    this.CreateChessEntry();
                }

                // toggle all other toggles to off
                var hubSection = toggle.FindParent<HubSection>();

                if (hubSection == null)
                {
                    return;
                }

                var toggles = hubSection.FindChildren<ToggleSwitch>().Where(t => t != toggle);
                foreach (var t in toggles)
                {
                    t.IsOn = false;
                }

                this.chessProject = p;

                Analytics.ReportEvent(EventCategory.Timing, EventAction.TimingStart, "chessTimer");
                this.chessStartTime = DateTime.UtcNow;
                this.timer.Change(1000, 1000);

                this.StartVisibility = Visibility.Collapsed;
                this.StopVisibility = Visibility.Collapsed;
            }
            else
            {
                if (this.chessProject != null && this.chessStartTime != null && toggle.DataContext == p)
                {
                    this.CreateChessEntry();
                }

                if (this.startTime == null)
                {
                    this.StartVisibility = Visibility.Visible;
                    this.StopVisibility = Visibility.Collapsed; 
                }  
            }
        }

        private void CreateChessEntry()
        {
            Debug.Assert(this.chessStartTime != null);
            this.CreateTimeEntry(this.chessStartTime.Value, DateTime.UtcNow, this.chessProject, null, true);

            this.DiscardChessTracking();
        }

        private void DiscardChessTracking()
        {
            this.timer.Change(Timeout.Infinite, Timeout.Infinite);
            this.IsChessRunning = false;
            this.chessProject = null;
            this.chessStartTime = null;
            this.ChessTrackingDuration = "00:00:00";
        }

        private async void ResumeEntryMenu_OnClick(object sender, RoutedEventArgs e)
        {
            if (this.Workspace == null)
            {
                await this.Dispatcher.ShowMessage("No workspace could be loaded, time entry resuming is disabled.", "Toggly");
                return;
            }

            if (this.startTime != null)
            {
                await this.Dispatcher.ShowMessage(
                    "You are already tracking a time entry. Tracking two activities at the same time is not allowed.",
                    "Toggly");
                this.Hub.ScrollToSection(this.Hub.Sections[0]);
                return;
            }

            if (this.chessStartTime != null)
            {
                await this.Dispatcher.ShowMessage(
                    "You are currently using the chess tracking. Tracking two activities at the same time is not allowed.",
                    "Toggly");
                this.Hub.ScrollToSection(this.Hub.Sections[this.Hub.Sections.Count - 1]);
                return;
            }

            var menuItem = sender as MenuFlyoutItem;

            if (menuItem == null)
            {
                return;
            }

            var entry = (TimeEntry)menuItem.DataContext;
            var md = new MessageDialog("Do you really wish to resume the selected time entry?", "Delete entry?");
            md.Commands.Add(
                new UICommand(
                    "yes",
                    o =>
                        {
                            this.TimeEntryDescription = entry.Description;

                            if (entry.TagIds != null && entry.TagIds.Any())
                            {
                                this.SelectedTags = this.workspace.Tags.Where(t => entry.TagIds.Contains(t.Name));
                            }
                            else
                            {
                                this.SelectedTags = null;
                            }

                            if (entry.ProjectId > 0)
                            {
                                this.SelectedProjects =
                                    this.Workspace.Projects.Where(p => p.Id == entry.ProjectId).ToArray();
                            }
                            else
                            {
                                this.selectedProjects = null;
                            }

                            this.StartLiveTracking();
                        }));

            md.Commands.Add(new UICommand("no"));
            md.CancelCommandIndex = 1;
            md.DefaultCommandIndex = 1;
            await md.ShowAsync(); 
        }

        private void DiscardButton_OnClick(object sender, RoutedEventArgs e)
        {
            Analytics.ReportEvent(EventCategory.Timing, EventAction.TimingDiscard, "liveTimer");
            this.DiscardTracking();
        }

        private void DiscardTracking()
        {
            this.timer.Change(Timeout.Infinite, Timeout.Infinite);
            this.StartVisibility = Visibility.Visible;
            this.StopVisibility = Visibility.Collapsed;
            this.startTime = null;
            this.TrackingDuration = "00:00:00";
            this.SelectedProjects = null;
            this.TimeEntryDescription = string.Empty;
            this.SelectedTags = null;
        }

        private void DiscardChessButton_OnClick(object sender, RoutedEventArgs e)
        {
            Analytics.ReportEvent(EventCategory.Timing, EventAction.TimingDiscard, "chessTimer");
            this.DiscardChessTracking();

            var toggles = this.Hub.Sections[this.Hub.Sections.Count - 1].FindChildren<ToggleSwitch>();
            foreach (var t in toggles)
            {
                t.IsOn = false;
            }
        }

        private async void ReloadWorkspaceButton_OnClick(object sender, RoutedEventArgs e)
        {
            await this.SyncWorkspace();
        }

        private async Task SyncWorkspace()
        {
            if (this.startTime.HasValue || this.chessStartTime.HasValue)
            {
                await this.Dispatcher.ShowMessage(
                    "You cannot reload your workspace while tracking time. Stop the tracking and try again.",
                    "Toggly");

                return;
            }

            if (Toggly.HasInternetAccess())
            {
                if (CacheLayer.HasPendingChanges())
                {
                    this.IsWorking = true;
                    this.OverlayMessage = "Saving changes...";
                    var flushResult = await CacheLayer.FlushWrites();

                    if (flushResult)
                    {
                        await CacheLayer.Scrub();
                    }
                    else
                    {
                        await
                            this.Dispatcher.ShowMessage(
                                "Toggly encountered problems while uploading your changes. Verify your internet connection and try again.",
                                "Toggly");
                    }
                }
                else
                {
                    await CacheLayer.Scrub();
                }
            }
            else
            {
                await
                    this.Dispatcher.ShowMessage(
                        "No internet connection detected. Reload will only retrieve the latest cached data.",
                        "Toggly");
            }

            await this.LoadWorkspace();
        }

        private void EditWorkspaceButton_OnClick(object sender, RoutedEventArgs e)
        {
            if (this.workspace == null)
            {
                return;
            }

            this.Frame.Navigate(typeof(WorkspacePage), this.workspace.Id);
        }

        private void ReportsButton_OnClick(object sender, RoutedEventArgs e)
        {
            if (this.Workspace == null)
            {
                this.Dispatcher.ShowMessage("No workspace could be loaded, reports are disabled.", "Toggly");
                return;
            }

            this.Frame.Navigate(typeof(ReportsPage), this.Workspace.Id);
        }

        private void AboutButton_OnClick(object sender, RoutedEventArgs e)
        {
            this.Frame.Navigate(typeof(AboutPage));
        }

        private async void SwitchWorkspaceButton_OnClick(object sender, RoutedEventArgs e)
        {
            if (this.startTime.HasValue || this.chessStartTime.HasValue)
            {
                await this.Dispatcher.ShowMessage(
                    "You cannot switch workspaces while tracking time. Stop the tracking and try again.",
                    "Toggly");

                return;
            }

            var provider = ServiceContainer.Resolve<IWorkspaceProvider>();
            this.Workspaces = await provider.GetWorkspaces();

            var obj = sender as FrameworkElement;

            if (obj == null)
            {
                return;
            }

            FlyoutBase.ShowAttachedFlyout(obj); 
        }

        private void WorkspaceList_OnSelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            var obj = sender as FrameworkElement;

            if (obj == null)
            {
                return;
            }

            FlyoutBase.GetAttachedFlyout(this.SwitchWorkspaceButton).Hide();

            if (!e.AddedItems.Any())
            {
                return;
            }

            var ws = e.AddedItems[0] as Workspace;

            if (ws == null)
            {
                return;
            }

            var settings = ServiceContainer.Resolve<IApplicationSettings>();

            settings.LastWorkspace = ws.Id;
            this.WorkspaceList.SelectedIndex = -1;
            this.LoadWorkspace();
        }
    }
}